home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Libraries / VideoToolbox 94.11.17 / Demos / TimeVideo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-17  |  31.6 KB  |  736 lines  |  [TEXT/KAHL]

  1. /*
  2. TimeVideo.c
  3. Copyright © Denis G. Pelli, 1992, 1993, 1994
  4.  
  5. TimeVideo does a thorough test of the timing and synchronization of each video
  6. screen, as well as the integrity of the clut hardware and software, and saves
  7. the results in a self-explanatory text file, “TimeVideo results”. A discussion
  8. of movies, lookup table animation, and synchronization appears in the
  9. VideoToolbox “Video synch” file. The many video driver bugs uncovered by
  10. TimeVideo and its predecessors are reported in that file. (For example, version 3 of the
  11. TrueVision NuVista driver assumes zero start in 16- and 32-bit modes.) Please
  12. add your results by emailing your “TimeVideo results” to denis_pelli@isr.syr.edu.
  13.  
  14. Despite Apple’s rules that require it, a few video drivers don’t allow you to
  15. read the clut (i.e. use GDGetEntries), or crash if you attempt it. GDGetEntries
  16. (in GDVideo.c) checks against a list of known offenders and returns a statusErr
  17. is the call is not usable. If we can’t use GDGetEntries then we fall back to a
  18. visual test of the clut, comparing a particular way of loading the clut against
  19. the standard way of loading the clut. Any visible change between these two
  20. conditions suggests an error in the non-standard way of loading the clut.
  21.  
  22. BUGS:
  23. Version 1.0 hung up on a PowerBook 170 with an Envisio video adapter. I didn’t 
  24. have a chance to figure out why. It works fine on my ordinary PowerBook 170.
  25.  
  26. HISTORY:
  27. 8/23/92    dgp    wrote it, based on my TimeCPU.c
  28. 8/26/92    dgp print the driver version only if it's nonzero.
  29.             added summary at end of printout.
  30. 9/9/92    dgp    changed printout of system vbl rate from %.1f to %.2f
  31.             Added movie rate to measurements and printout.
  32. 9/15/92    dgp    made compatible with System 6.04. Added header to results file.
  33. 9/17/92    dgp    Added QUICKLY switch, to select CopyBits or CopyBitsQuickly.
  34. 10/5/92    dgp    cosmetic changes
  35. 10/6/92    dgp    report program version. Use frames/clut update as the criterion for summary.
  36. 10/9/92    dgp    Fixed summary. Too slow means MORE than one frame per clut update.
  37.             Fixed restoration of clut in direct modes (i.e. 16 and 32 bit modes).
  38.             Renamed to GDRestoreDeviceClut and put it in GDVideo.c.
  39. 10/13/92 dgp Tom Busey reported that frames were going uncounted
  40.             during the clut timing, which seems to be a problem with some video drivers.
  41.             TimeVideo now reports a frame count based solely on timing in secs and
  42.             the separately measured frame rate, which seems to be a reliable.
  43.             It also double checks the timing in secs vs frames, and if
  44.             it finds a discrepancy, prints a warning to the screen & file. 
  45. 10/20/92 dgp Added VBL/frame to summary.
  46. 12/8/92    dgp Minor editing of comments.
  47. 12/9/92    dgp    1.01 Added _atexit(RestoreCluts) in case user quits prematurely.
  48. 12/11/92 dgp 1.02 GDRestoreBlackAndWhite allocates stack space for linearTable
  49.             only if we need it.
  50. 12/30/92 dgp 1.03 cosmetic changes.
  51. 12/30/92 dgp 1.04 Enhanced summary to account for NAN when no clut access is allowed.
  52. 12/30/92 dgp 1.05 Use GDClutSize().
  53. 1/6/93    dgp     1.06 Simplified GDRestoreBlackAndWhite, eliminated linearTable.
  54. 1/7/93    dgp  1.07 Included in VideoToolbox-1-93.sea
  55. 1/8/93    dgp     1.08 Added Info-Mac to report file.
  56. 1/11/93    dgp     1.09 In response to bug report from jonathan brecher, added support
  57.             for computers that lack Color QuickDraw.
  58. 1/15/93    dgp    1.10 Added version resource.
  59. 1/18/93    dgp    1.11 Updated the explanatory text.
  60. 1/24/93 dgp    1.12 Updated the explanatory text.
  61. 2/6/93 dgp    1.13 Report ROM version.
  62. 2/15/93 dgp    1.14 Report ROM version as 124+6*256.
  63. 2/22/93    dgp    1.15 Recompiled.
  64. 2/27/93    dgp    1.16 Recompiled with new Identify.c.
  65. 3/10/93    dgp    1.17 Choose a valid mode for which to print the frame rate.
  66.                  Moved all the timing code into GDInfo.c, so this routine just prints.
  67. 3/12/93    dgp        Reorganized the printout and added TestCluts's capabilities.
  68. 3/13/93    dgp        Added high-priority timing.
  69. 3/16/93    dgp    2.0 Added visual hash inspection.
  70. 3/31/93    dgp    2.1 Release version, clut test seems to work.
  71. 4/5/93    dgp    2.2 Recognize gray1 error.
  72. 4/6/93    dgp    2.3 Remove assumption that mode numbers imply certain number of bits.
  73. 4/13/93    dgp    2.3.1 Only set results file type when it is created, not when appending.
  74. 4/15/93    dgp        Display Testing window with gray wedge on test screen.
  75. 4/16/93    dgp    2.4b Restore compatibility with 1-bit qd.
  76. 4/19/93    dgp    2.4.1b Support old Mac II 24-bit NuBus addressing. Fixed two places
  77.                 where I used garbage in place of a linear color table when
  78.                 gdType==directType. Use new GDNewLinearColorTable.
  79. 4/19/93    dgp    2.4.2b Estimate color transformation matrix.
  80. 4/19/93    dgp    2.4.3b Use color transformation matrix to assess gray error.
  81. 4/19/93    dgp    2.4.4b Fixed RectToAddress to always return a 32-bit address,
  82.                     so it won't crash under System 6.
  83. 4/25/93    dgp    2.4.5b Correct for -0.5 bias in estimating rgb gains in TestClut.c
  84. 4/25/93    dgp    2.4.6b Test both color and gray modes; tolerance=3 lsb.
  85. 4/25/93    dgp    2.4.7b Worked around New Palette Manager bugs in original 32-bit QD.
  86. 4/27/93    dgp    2.4.8b Fixed stale-cache bug in SetPixelsQuickly. Cleaned up mode
  87.                     indexing.
  88. 4/28/93    dgp 2.4.9b Rewrote code dealing with SetDepth and HasDepth, wrote GDHasMode().
  89.                     Ignore "isGray" for 16 and 32-bit modes.
  90. 5/11/93    dgp 2.4.9bb Fixed bug in GDTestClutHash in GDTestClut.c in response to bug 
  91.                 report by Jonathan Brecher.
  92. 5/17/93    dgp 2.4.9bbb Changed SetEntriesQuickly to respect the color/gray mode of device.
  93. 5/18/93    dgp 2.4.9b4 Check that card->device is not NULL.
  94. 5/18/93    dgp 2.4.9b5 Suppress printing of identity color transformation.
  95. 5/18/93    dgp 2.4.9b6 Note that 16 and 32 bit depths are always in color mode.
  96. 5/18/93    dgp 2.5 Release version.
  97. 6/2/93    dgp 2.5.1 Updated text reference to info-mac.
  98. 7/7/93    dgp 2.5.2 Recompiled with new GDDacSize, for compatibility with
  99.                 Radius PowerView.
  100. 1/13/94    dgp 2.5.3 Updated description of VideoToolbox.
  101. 6/7/94    dgp    3.0.0 New C-based version of CopyBitsQuickly. Fixed versions of CopyBitsQuickly,
  102.                 SetPixelsQuickly, and SetEntriesQuickly that are compatible with dirty ROMs.
  103. 6/14/94    dgp    3.0.1 Copy Monaco-font style resource from application into the "TimeVideo report"
  104.                 document.
  105. 6/19/94    dgp    3.0.2 Various cosmetic changes, recompile with latest CopyBitsQuickly and 
  106.                 SetEntriesQuickly.
  107. 7/8/94    dgp    3.0.3 Recompiled with new THINK C 7.03.
  108. 7/29/94 dgp Eliminated use of "#s" printf format, since it's not supported by
  109.             Metrowerks CodeWarrior C.
  110. 8/1/94    dgp 3.0.4 Recompiled with latest sources.
  111. 8/14/94    dgp Added workaround for [][][][!isGray] bug in Metrowerks CodeWarrior compiler.
  112. 9/5/94 dgp removed assumption in printf's that int==short.
  113. 10/12/94 dgp Used Metrowerks CodeWarrior C to produce fat binary.
  114. 10/20/94 dgp 3.5 Defined BLANKLINE in GDInfo.h to work around bug in 
  115.         Metrowerks CW4.5 bug in handling of \r.
  116. 11/17/94    dgp    3.6 updated the text describing VideoToolbox.
  117. */
  118. #include "VideoToolbox.h"
  119. #include <Packages.h>
  120. #include <Traps.h>
  121. #include <math.h>
  122. #include <assert.h>
  123. #include "GDInfo.h"
  124. void TimeVideo(void);
  125. void PrintVideoInfo(FILE *o[2],VideoInfo *card);
  126. void ReportRGBGains(FILE *o[2],Boolean quickly,Boolean isGray,VideoInfo *card);
  127. long GDColors(GDHandle device);
  128. char setEntriesString[][18]={"GDSetEntries","SetEntriesQuickly"};
  129. char colorGrayString[][6]={"color","gray"};
  130. char ColorGrayString[][6]={"Color","Gray"};
  131. #define VERSION "3.6"
  132.  
  133. void main(void)
  134. {
  135.     StackGrow(20000+MAX_SCREENS*sizeof(VideoInfo));
  136.     Require(gestaltOriginalQD);
  137.     TimeVideo();
  138. }
  139.  
  140. void TimeVideo(void)
  141. {
  142.     long system;
  143.     unsigned long v[256],colorMax;
  144.     FILE *o[2],*dataFile;
  145.     int newDataFile;
  146.     unsigned long time;
  147.     short *p;
  148.     Str255 todayStr;
  149.     int i,j,d,error,oldPixelSize,oldIsColor,oldMode,cards,width,height;
  150.     int quickly,isGray,doTest,ok;
  151.     Rect r,screenRect;
  152.     WindowPtr window;
  153.     short doVisualTests,flags;
  154.     VideoInfo *card;
  155.     Handle rsrc;
  156.     int rsrcFile;
  157.     char string[999],datafilename[]="TimeVideo results";
  158.     
  159.     assert(StackSpace()>5000);
  160.     MaximizeConsoleHeight();
  161.     #if (THINK_C || THINK_CPLUS)
  162.         console_options.title="\pTimeVideo";
  163.     #endif
  164.     printf("\n");    // ask THINK C to initialize quickdraw
  165.     GetDateTime(&time);
  166.     srand(time);
  167.     srandU(time);
  168.     sprintf(string,"Welcome to TimeVideo " VERSION "\n\n"
  169.     "This program will thoroughly test all your video devices and save the "
  170.     "results in the text file “%s”. Don’t be alarmed by the strange "
  171.     "antics of your screens. Everything will soon be back to normal. Just sit back "
  172.     "and enjoy the show.",datafilename);
  173.     sprintf(string,"%s You may quit at any time by hitting Command-period.",string);
  174.     printf(BreakLines(string,78));
  175.     printf("\n\nWould you like the testing to pause for visual inspections?");
  176.     doVisualTests=YesOrNo(1);
  177.     printf("\n");
  178.     if(0)printf(BreakLines("\n"
  179.         "TimeVideo times everything: the video frames, interrupts, and cluts. And it "
  180.         "determines what fraction of the screen you can fill with a real-time movie shown "
  181.         "by CopyBits() or CopyBitsQuickly(). Then it does write-then-read tests of the "
  182.         "clut, to make sure the hardware and software, GDSetEntries or SetEntriesQuickly, "
  183.         "are working correctly.\n",78));
  184.     printf("\n");
  185.     dataFile=fopen(datafilename,"r");
  186.     newDataFile=(dataFile==NULL);
  187.     if(newDataFile){
  188.         // try to copy SimpleText style resource from TimeVideo application into our document.
  189.         rsrc=GetResource('styl',128);
  190.         if(rsrc!=NULL){
  191.             c2pstr(datafilename);
  192.             CreateResFile((unsigned char *)datafilename);
  193.             rsrcFile=OpenResFile((unsigned char *)datafilename);
  194.             p2cstr((unsigned char *)datafilename);
  195.             if(rsrcFile!=-1){
  196.                 DetachResource(rsrc);
  197.                 UseResFile(rsrcFile);
  198.                 AddResource(rsrc,'styl',128,(ConstStr255Param)"/pTimeVideo report style");
  199.                 CloseResFile(rsrcFile);
  200.             }else ReleaseResource(rsrc);
  201.         }
  202.     }else fclose(dataFile);
  203.     o[0]=stdout;
  204.     o[1]=dataFile=fopen(datafilename,"a");    /* Append to data file */
  205.     if(dataFile==NULL){
  206.         printf("Could neither open nor create “%s”. Perhaps your disk is locked.\n"
  207.             ,datafilename);
  208.         newDataFile=0;
  209.     }
  210.     if(newDataFile){
  211.         SetFileInfo(datafilename,'TEXT','ttxt');
  212.         fprintf(dataFile,BreakLines(
  213.         "This file reports the timing and accuracy of all your video screens, "
  214.         "as measured by TimeVideo, a component of the VideoToolbox. To quickly "
  215.         "test a large number of computers, run TimeVideo from a floppy disk; "
  216.         "all the results will accumulate in a single results file.\n"
  217.         "\nTHE VIDEO TOOLBOX\n"
  218.         "The VideoToolbox is a collection of two hundred C subroutines and several "
  219.         "demo and utility programs that I and others have written to do visual "
  220.         "psychophysics with Macintosh computers. It is fully compatible with 680x0 "
  221.         "and Power PC Macs and Symantec THINK C 6 or 7 and Metrowerks CodeWarrior C "
  222.         "4.5 compilers. It's free and may not be sold without permission. It should "
  223.         "be useful to anyone who wants to present accurately specified visual "
  224.         "stimuli or use the Mac for psychometric experiments. The text file \"Video "
  225.         "synch\" discusses all the ways of synchronizing programs to video displays "
  226.         "and the many pitfalls to avoid. The TimeVideo application checks out the "
  227.         "timing of all video devices in anticipation of their use in critical "
  228.         "real-time applications, e.g. movies or lookup table animation. Low-level "
  229.         "routines control video timing and lookup tables, display real-time movies, "
  230.         "and implement the luminance-control algorithms suggested by Pelli and "
  231.         "Zhang (1991). (D.G. Pelli and L. Zhang, 1991, \"Accurate control of "
  232.         "contrast on microcomputer displays.\" Vision Research, 31, 1337-1350. "
  233.         "Reprints are available.) In particular, GetPixelsQuickly and "
  234.         "SetPixelsQuickly peek and poke pixels in bitmaps and pixmaps, "
  235.         "CopyBitsQuickly and CopyWindows faithfully copy between bit/pixmaps and "
  236.         "the screen, WindowToEPS saves an image to disk, and SetEntriesQuickly and "
  237.         "GDSetEntries load the screen's color lookup table, all without any of "
  238.         "QuickDraw's color translations. High-level routines help analyze "
  239.         "psychophysical experiments (e.g. graphing or maximum-likelihood fitting of "
  240.         "psychometric data). Assign.c is a runtime C interpreter for C assignment "
  241.         "statements, which is useful for controlling experiments and sharing "
  242.         "calibration data. This collection has been continually updated since 1991. "
  243.         "Many colleagues have indicated that they are using the software in their "
  244.         "labs. Documentation is in the source files themselves. Many of the "
  245.         "routines are Mac-specific, but some very useful routines, e.g. the "
  246.         "luminance-control, statistics, maximum-likelihood fitting algorithms, and "
  247.         "the runtime interpreter are written in Standard C and will work on any "
  248.         "computer. "
  249.         "Documentation is in the source files themselves. "
  250.         "To get the latest version of the VideoToolbox just download "
  251.         "“video-toolbox” electronically from a public archive:\n"
  252.         "ftp://sumex-aim.stanford.edu/info-mac/dev/src/\n"
  253.         "ftp://grind.isca.uiowa.edu/mac/infomac/dev/src/\n"
  254.         "ftp://ftp.uu.net/archive/systems/mac/info-mac/dev/src/\n"
  255.         "ftp://amug.org/pub/ftp1/info-mac/dev/src/\n"
  256.         "ftp://wuarchive.wustl.edu/systems/mac/info-mac/dev/src/\n"
  257.         "ftp://src.doc.ic.ac.uk/packages/mac/info-mac/dev/src/\n"
  258.         "ftp://ftp.stolaf.edu/pub/macpsych/\n"
  259.         "Log in as “anonymous”; any password will do. "
  260.         "It's also on CompuServe in the MacDev forum's Library 4 “C and Pascal” "
  261.         "as VIDEOT.SEA. "
  262.         "If you're not electronically connected, send me your postal address "
  263.         "and I'll mail you a disk.\n",78));
  264.     fprintf(dataFile,
  265.         "Denis Pelli, Professor of Neuroscience, "
  266.         "Institute for Sensory Research, Syracuse University, Syracuse, NY 13244-5290, USA, "
  267.         "denis_pelli@isr.syr.edu\n");
  268.     fprintf(dataFile,BreakLines(
  269.         "\nTIME VIDEO\n"
  270.         "For each video card, TimeVideo measures the video frame rate, frequency of VBL "
  271.         "interrupts (ought to be one per frame), how long it takes to load the clut, "
  272.         "and how much of the screen you can fill with a real-time one-image-per-frame "
  273.         "movie shown by CopyBits() or CopyBitsQuickly(). It then performs a random "
  274.         "write-then-read test of the Color Lookup Table (clut). This tests the clut "
  275.         "memory hardware and the software used to write and read the clut. We test "
  276.         "writing by GDSetEntries(), which passes the request on to the video driver, and, "
  277.         "if possible, we also test writing by SetEntriesQuickly(), which accesses the hardware "
  278.         "directly. (SetEntriesQuickly supports only a few video cards.) "
  279.         "In either case, the clut is read by GDGetEntries(), which passes the "
  280.         "request on to the video driver. The testing is thorough; many video devices fail "
  281.         "at least part of the test. All the driver errors uncovered to date appear in the "
  282.         "VideoToolbox “Video synch” text file, and have been reported to the video card "
  283.         "manufacturer. Add your results by emailing this file to "
  284.         "denis_pelli@isr.syr.edu\n"
  285.         "\nErrors reported by TimeVideo are usually due to bugs in the video driver software "
  286.         "in the ROM of the video card or built-in video. If the bug will "
  287.         "interfere with your experiments, "
  288.         "then to use the card (or built-in video) you must either fix/replace the driver or "
  289.         "bypass the driver, using SetEntriesQuickly to access the hardware directly "
  290.         "(if SetEntriesQuickly supports that video card or built-in video). "
  291.         "I suggest that you try replacing the driver, because this will keep your "
  292.         "software hardware-independent. The VideoToolbox \"Video synch\" document explains "
  293.         "how to fix the the Mac IIci video driver, patching or replacing the buggy version "
  294.         "0 .Display_Video_Apple_RBV1 driver by copying the bug-free version 1 of the same "
  295.         "driver from the Mac IIsi. It is very likely that an analogous approach "
  296.         "could be used to fix/replace the buggy version 0, 1, and 2 .Display_Video_Apple_DAFB "
  297.         "drivers in the Quadra 700, 750, and 900, by the bug-free version 3 or 5 of that driver "
  298.         "in the Centris 650 or the LC 475. And please report your bugs; the bug-free revised drivers "
  299.         "are probably the result of our past bug reports.\n"
  300.         "\nGLOSSARY\n"
  301.         "A video frame is a refresh of your video screen. To show a movie, you will want "
  302.         "to reload the image once per frame; the table shows how big that image can be, "
  303.         "as a fraction of the screen area, and still be reloaded once per frame. "
  304.         "(Some video cards have multiple video “pages” that can be switched by "
  305.         "calling GDSetMode(), though I've never tried it.) A “VBL” "
  306.         "interrupt is produced by your video card driver, nominally once per frame, but "
  307.         "some video drivers produce more, which is poor, but not serious. (The "
  308.         "VBLInstall.c program will deal with it.) Suppressed interrupts during clut "
  309.         "updates are bad. It means that the driver disables the VBL interrupt for too "
  310.         "long while it’s loading the clut. This will throw off any interrupt-based "
  311.         "attempt to count frames. The clut is the color lookup table of your video card. "
  312.         "Lookup table animation, e.g. for temporal modulation of contrast, requires that "
  313.         "you reload the clut once per frame, so it’s very important that this be fast "
  314.         "enough. "
  315.         "The first call to SetEntriesQuickly() for each device is slow--a cache is "
  316.         "filled; the reported times are for subsequent calls. "
  317.         "Each video card can be in “Color” or “Gray” mode, as set by the Control "
  318.         "Panel:Monitors or the Macintosh Toolbox call SetDepth(). "
  319.         "In “Gray” mode all colors are transformed to luminance-equivalent "
  320.         "grays. This is done by the video driver, when loading the clut, "
  321.         "but only if the pixelSize≤8. "
  322.         "TimeVideo measures the driver's color transformation matrix, and reports it "
  323.         "if it is other than the identity transformation:\n"
  324.         "(ROut) (1 0 0) (RIn)\n"
  325.         "(GOut)=(0 1 0)x(GIn)\n"
  326.         "(BOut) (0 0 1) (BIn)\n"
  327.         "The clut tests try loading the clut serially, one entry at a time, and "
  328.         "all at once; some video drivers fail the serial test. For more explanation see "
  329.         "the text file called “Video synch” on the VideoToolbox disk.\n"
  330.         "\n"
  331.         "“ok”= passed all tests.\n"
  332.         "“!gray”= passed the color test, but is supposed to be in gray mode.\n"
  333.         "“!color”= passed the gray test, but is supposed to be in color mode.\n"
  334.         "“!serial”= passed when loaded all at once, but failed when loaded serially.\n"
  335.         "“bad”= read did not equal write and the error is reported explicitly.\n"
  336.         "We know that SetEntriesQuickly is “!serial” on the Quadra, alas.\n"
  337.         "\nThis is a SimpleText document. You can use TeachText in a pinch, but "
  338.         "it won't line up the columns properly.\n\n",78));
  339.     }
  340.     ffprintf(o,"TimeVideo version " VERSION "\n");
  341.     GetDateTime(&time);
  342.     IUDateString(time,longDate,todayStr);
  343.     ffprintf(o,"%s.\n",p2cstr(todayStr));
  344.     ffprintf(o,"%s\n",IdentifyCompiler());
  345.  
  346.     // Timer.c requires the Revised Time Manager, which appeared in System 6.0.3.
  347.     // SetDepth() is part of the "new" Palette Manager, which appeared in System 6.0.5.
  348.     Gestalt(gestaltSystemVersion,&system);
  349.     if(system<0x605) {
  350.         PrintfExit("Sorry. Your System is too old; I need at least System 6.0.5.\n");
  351.     }
  352.     card=(VideoInfo *)NewPtr(MAX_SCREENS*sizeof(*card));
  353. //PrintfExit("MAX_SCREENS*sizeof(*card) %ld\n",MAX_SCREENS*sizeof(*card));
  354.     if(card==NULL)PrintfExit("Need more memory. "
  355.         "Use Finder's FIle:Get Info to increase TimeVideo's allocation.\n");
  356.     for(i=0,p=(short *)card;i<GetPtrSize((Ptr)card)/2;i++)*p++=0;    // Zero the cards.
  357.  
  358.     ffprintf(o,"%s\n",BreakLines(IdentifyMachine(),78));
  359.     ffprintf(o,"Tick rate is %.1f Hz.",TickRate());
  360.     ffprintf(o," System-based VBL rate is %.1f Hz.\n",GDFrameRate(NULL));
  361.     if(QD8Exists())_atexit(RestoreCluts);// In case user quits prematurely.
  362.     for(i=0;;i++){
  363.         card[i].basicTested=0;
  364.         card[i].timeTested=0;
  365.         card[i].clutTested=0;
  366.         for(d=0;d<6;d++){
  367.             card[i].depth[d].timeTested=0;
  368.             for(quickly=0;quickly<2;quickly++)for(isGray=0;isGray<2;isGray++){
  369.                 card[i].depth[d].clut[quickly][isGray].read.doTest=0;
  370.                 card[i].depth[d].clut[quickly][isGray].read.tested=0;
  371.                 card[i].depth[d].clut[quickly][isGray].read.errors=0;
  372.                 card[i].depth[d].clut[quickly][isGray].read.errorsAtOnce=0;
  373.                 card[i].depth[d].clut[quickly][isGray].hash.tested=0;
  374.                 card[i].depth[d].clut[quickly][isGray].hash.doTest=0;
  375.                 card[i].depth[d].clut[quickly][isGray].visual.tested=0;
  376.                 card[i].depth[d].clut[quickly][isGray].visual.doTest=0;
  377.             }
  378.         }
  379.         if(QD8Exists()){
  380.             card[i].device=GetScreenDevice(i);
  381.             if(card[i].device==NULL)break;
  382.             oldPixelSize=(**(**card[i].device).gdPMap).pixelSize;
  383.             oldMode=(**card[i].device).gdMode;
  384.             oldIsColor=TestDeviceAttribute(card[i].device,gdDevType);
  385.         }else card[i].device=NULL;
  386.         ffprintf(o,"\n%s\n",BreakLines(IdentifyVideo(card[i].device),78));
  387.         printf("Getting card info. . ." "\r");
  388.         error=GDInfo(&card[i]);
  389.         
  390.         // Specify what tests we want.
  391.         for(d=0;d<6;d++)for(quickly=0;quickly<2;quickly++)for(isGray=0;isGray<2;isGray++){
  392.             card[i].depth[d].clut[quickly][isGray].read.doTest=1;
  393.         }
  394.         if(card[i].device!=NULL)isGray=!TestDeviceAttribute(card[i].device,gdDevType);
  395.         else isGray=0;
  396.         // Further test deepest directType and clutType modes
  397.         d=5;
  398.         while(card[i].depth[d].pixelSize>=0 && card[i].depth[d].pixelSize<16)d--;
  399.         if(d>=0)for(quickly=0;quickly<2;quickly++){
  400.             int notIsGray=!isGray;
  401.             card[i].depth[d].clut[quickly][isGray].hash.doTest=doVisualTests;
  402.             card[i].depth[d].clut[quickly][notIsGray].read.doTest=1;
  403.         }
  404.         d=3;
  405.         while(card[i].depth[d].pixelSize==0 || card[i].depth[d].pixelSize>8)d--;
  406.         if(d>=0)for(quickly=0;quickly<2;quickly++){
  407.             int notIsGray=!isGray;
  408.             card[i].depth[d].clut[quickly][isGray].hash.doTest=doVisualTests;
  409.             card[i].depth[d].clut[quickly][notIsGray].read.doTest=1;
  410.         }
  411.  
  412.         // Open window
  413.         if(QD8Exists()){
  414.             r=screenRect=(*card[i].device)->gdRect;
  415.         }else{
  416.             CopyQuickDrawGlobals();    // make sure qd is valid.
  417.             r=screenRect=qd.screenBits.bounds;
  418.         }
  419.         OffsetRect(&r,-r.left,-r.top);
  420.         width=(r.right/=3);
  421.         if(width>256)width=r.right=256;
  422.         height=r.bottom=width;
  423.         CenterRectInRect(&r,&screenRect);
  424.         if(QD8Exists())window=(WindowPtr)NewCWindow(NULL,&r,"\pCLUT Colors",TRUE,noGrowDocProc,(WindowPtr) -1L,0,0);
  425.         else window=NewWindow(NULL,&r,"\pTesting",TRUE,noGrowDocProc,(WindowPtr) -1L,0,0);
  426.             
  427.         for(d=0;d<6;d++)for(isGray=0;isGray<2;isGray++){
  428.             if(card[i].depth[d].pixelSize==0)continue;
  429.             doTest=0;
  430.             for(quickly=0;quickly<2;quickly++){
  431.                 doTest|=card[i].depth[d].clut[quickly][isGray].read.doTest;
  432.                 doTest|=card[i].depth[d].clut[quickly][isGray].hash.doTest;
  433.                 doTest|=card[i].depth[d].clut[quickly][isGray].visual.doTest;
  434.             }
  435.             if(!doTest)continue;
  436.             if(card[i].device!=NULL){
  437.                 if(card[i].depth[d].mode!=(**card[i].device).gdMode
  438.                     || isGray!=!TestDeviceAttribute(card[i].device,gdDevType)){
  439.                     // On Mac IIci, Sys 6.07, HasDepth returns "mode" of 0x100 
  440.                     // at all legal depths.
  441.                     if(card[i].depth[d].pixelSize>0)
  442.                         ok=HasDepth(card[i].device,card[i].depth[d].pixelSize,1,!isGray);
  443.                     else ok=1;
  444.                     // Mac IIci Sys 6.07: SetDepth only accepts mode, not pixelSize.
  445.                     if(ok)error=SetDepth(card[i].device,card[i].depth[d].mode,1,!isGray);
  446.                     if(card[i].depth[d].mode!=(**card[i].device).gdMode
  447.                         || isGray!=!TestDeviceAttribute(card[i].device,gdDevType))continue;
  448.                 }
  449.             }else if(card[i].depth[d].pixelSize!=1 || isGray)continue;
  450.             GDInfo(&card[i]);
  451.             
  452.             // Fill window with spectrum of all colors.
  453.             colorMax=GDColors(card[i].device)-1;
  454.             for(j=0;j<width;j++)v[j]=(j*colorMax+width/2)/(width-1);
  455.             for(j=0;j<height;j++)SetWindowPixelsQuickly(window,0,j,v,width);
  456.             
  457.             if(!card[i].depth[d].timeTested){
  458.                 error=GDInfoTime(&card[i]);
  459.                 for(j=0;j<height;j++)SetWindowPixelsQuickly(window,0,j,v,width);//refresh
  460.             }
  461.             for(quickly=0;quickly<2;quickly++){
  462.                 printf(BLANKLINE);
  463.                 printf("%d-bit %s pixels: testing clut: %s . . ." "\r"
  464.                     ,(int)card[i].depth[d].pixelSize,colorGrayString[isGray]
  465.                     ,setEntriesString[quickly]);
  466.                 if(quickly)flags=testClutQuicklyFlag;
  467.                 else flags=0;
  468.                 error=GDTestClut(o,flags,&card[i]);
  469.                 error=GDTestClut(o,flags|testClutSeriallyFlag,&card[i]);
  470.                 error=GDTestClutHash(flags,&card[i]);
  471.             }
  472.         }
  473.         DisposeWindow(window);
  474.         if(QD8Exists()){
  475.             // On Mac IIci, Sys 6.07, HasDepth returns "mode" of 0x100 at all legal depths.
  476.             // and SetDepth only accepts mode, not depth.
  477.             ok=HasDepth(card[i].device,oldPixelSize,1,oldIsColor);
  478.             if(ok)error=SetDepth(card[i].device,oldMode,1,oldIsColor);
  479.         }
  480.         printf(BLANKLINE);
  481.         printf("Getting card info. . ." "\r");
  482.         error=GDInfo(&card[i]);
  483.         printf(BLANKLINE);
  484.         PrintVideoInfo(o,&card[i]);
  485.         if(!QD8Exists())break;
  486.     }
  487.     cards=i;
  488.     if(GDVersion(card[i].device)==100
  489.         && EqualString("\p.Display_Video_Apple_RBV1",GDName(card[i].device),1,1)){
  490.             ffprintf(o,
  491.             "NOTE: the built-in driver in the Mac IIci (.Display_Video_Apple_RBV1 version 0)\n"
  492.             "has a bug that causes it to crash if you attempt to read the clut. A temporary\n"
  493.             "patch has been applied that has fixed the driver until the next reboot, as\n"
  494.             "explained in the VideoToolbox file “Video synch”.\n");
  495.     }
  496.     if(dataFile!=NULL){
  497.         fprintf(dataFile,"\n\n");
  498.         fclose(dataFile);
  499.         sprintf(string,"\nThe text file “%s” explains all the results.\n",datafilename);
  500.         printf(BreakLines(string,78));
  501.     }
  502.     DisposePtr((Ptr)card);
  503. }
  504.  
  505. void PrintVideoInfo(FILE *o[2],VideoInfo *card)
  506. {
  507.     short i,d,tested,quickly,isGray,grayClut,reportRGB;
  508.     char string[100];
  509.     static char s1[]="%-33s",s2[]="%6s",s3[]="  %-9s\n";
  510.     VideoCardClutTest *clut;
  511.     
  512.     if(card->device!=NULL)isGray=!TestDeviceAttribute(card->device,gdDevType);
  513.     else isGray=0;
  514.     ffprintf(o,"%d-bit dacs. ",(int)card->dacSize);
  515.     ffprintf(o,"%dx%d pixels. ",(int)card->width,(int)card->height);
  516. //    ffprintf(o,"%s mode. ",ColorGrayString[isGray]);
  517.     ffprintf(o,"\n");
  518.  
  519.     if(card->basicTested){
  520.         ffprintf(o,s1,"pixel size");
  521.         for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  522.             sprintf(string,"%d   ",(int)card->depth[d].pixelSize);
  523.             ffprintf(o,s2,string);
  524.         }
  525.         ffprintf(o,s3,"bits");
  526.     
  527.         if(QD8Exists()){
  528.             // I haven't tried to look for 1-bit QD's page swapping scheme
  529.             ffprintf(o,s1,"pages");
  530.             for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  531.                 sprintf(string,"%d   ",(int)card->depth[d].pages);
  532.                 ffprintf(o,s2,string);
  533.             }
  534.             ffprintf(o,s3,"");
  535.     
  536.             // Meaningless in 1-bit QD
  537.             ffprintf(o,s1,"mode");
  538.             for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  539.                 sprintf(string,"0x%x ",card->depth[d].mode);
  540.                 ffprintf(o,s2,string);
  541.             }
  542.             ffprintf(o,s3,"");
  543.         }
  544.     }
  545.  
  546.     if(card->timeTested){
  547.         ffprintf(o,s1,"frame rate");
  548.         for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  549.             sprintf(string,"%.1f ",card->depth[d].frameRate);
  550.             ffprintf(o,s2,string);
  551.         }
  552.         ffprintf(o,s3,"Hz");
  553.     
  554.         ffprintf(o,s1,"interrupts per frame");
  555.         for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  556.             sprintf(string,"%.1f ",card->depth[d].vblPerFrame);
  557.             ffprintf(o,s2,string);
  558.         }
  559.         ffprintf(o,s3,"");
  560.     
  561.         ffprintf(o,s1,"CopyBits movie size");
  562.         for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  563.             sprintf(string,"%.2f",card->depth[d].movieRate/card->depth[d].frameRate);
  564.             ffprintf(o,s2,string);
  565.         }
  566.         ffprintf(o,s3,"screen");
  567.     
  568.         ffprintf(o,s1,"CopyBitsQuickly movie size");
  569.         for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  570.             sprintf(string,"%.2f",card->depth[d].movieRateQuickly/card->depth[d].frameRate);
  571.             ffprintf(o,s2,string);
  572.         }
  573.         ffprintf(o,s3,"screen");
  574.     
  575.         ffprintf(o,s1,"CopyBits data rate");
  576.         for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  577.             sprintf(string,"%.2f",card->depth[d].movieRate
  578.                 *card->depth[d].pixelSize
  579.                 *card->width*card->height/8./1024./1024.);
  580.             ffprintf(o,s2,string);
  581.         }
  582.         ffprintf(o,s3,"MB/s");
  583.     
  584.         ffprintf(o,s1,"CopyBitsQuickly data rate");
  585.         for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  586.             sprintf(string,"%.2f",card->depth[d].movieRateQuickly
  587.                 *card->depth[d].pixelSize
  588.                 *card->width*card->height/8./1024./1024.);
  589.             ffprintf(o,s2,string);
  590.         }
  591.         ffprintf(o,s3,"MB/s");
  592.     }
  593.     
  594.     if(card->device!=NULL && (**card->device).gdType!=fixedType && card->timeTested){
  595.         ffprintf(o,s1,"GDSetEntries duration");
  596.         for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  597.             sprintf(string,"%.2f",card->depth[d].framesPerClutUpdate);
  598.             ffprintf(o,s2,string);
  599.         }
  600.         ffprintf(o,s3,"frames");
  601.  
  602.         ffprintf(o,s1,"GDSetEntries suppresses ints. for");
  603.         for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  604.             sprintf(string,"%.1f ",card->depth[d].missingFramesPerClutUpdate);
  605.             ffprintf(o,s2,string);
  606.         }
  607.         ffprintf(o,s3,"frames");
  608.  
  609.         ffprintf(o,s1,"GDSetEntriesHighPriority duration");
  610.         for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  611.             sprintf(string,"%.2f",card->depth[d].framesPerClutUpdateHighPriority);
  612.             ffprintf(o,s2,string);
  613.         }
  614.         ffprintf(o,s3,"frames");
  615.  
  616.         if(card->setEntriesQuickly){
  617.             ffprintf(o,s1,"SetEntriesQuickly duration");
  618.             for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  619.                 sprintf(string,"%.2f",card->depth[d].framesPerClutUpdateQuickly);
  620.                 ffprintf(o,s2,string);
  621.             }
  622.             ffprintf(o,s3,"frames");
  623.         }
  624.     }
  625.     if(card->device!=NULL && (**card->device).gdType!=fixedType && card->clutTested){
  626.         for(quickly=0;quickly<2;quickly++)for(isGray=0;isGray<2;isGray++){
  627.             tested=0;
  628.             for(d=0;d<6;d++)if(card->depth[d].pixelSize)
  629.                 if(card->depth[d].clut[quickly][isGray].hash.tested)tested=1;
  630.             if(tested){
  631.                 sprintf(string,"%s hash inspection",setEntriesString[quickly]);
  632.                 ffprintf(o,s1,string);
  633.                 for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  634.                     clut=&card->depth[d].clut[quickly][isGray];
  635.                     if(clut->hash.tested){
  636.                         if(clut->hash.errors)
  637.                             sprintf(string,"fail");
  638.                         else sprintf(string,"ok");
  639.                     }else sprintf(string,"");
  640.                     ffprintf(o,s2,string);
  641.                 }
  642.                 ffprintf(o,s3,"");
  643.             }
  644.         }
  645.     
  646.         for(quickly=0;quickly<2;quickly++)for(isGray=0;isGray<2;isGray++){
  647.             tested=0;
  648.             for(d=0;d<6;d++)if(card->depth[d].pixelSize)
  649.                 if(card->depth[d].clut[quickly][isGray].read.tested)tested=1;
  650.             if(tested){
  651.                 reportRGB=0;
  652.                 sprintf(string,"%s: %s test"
  653.                     ,colorGrayString[isGray],setEntriesString[quickly]);
  654.                 ffprintf(o,s1,string);
  655.                 for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  656.                     clut=&card->depth[d].clut[quickly][isGray];
  657.                     grayClut=isGray && card->depth[d].pixelSize<=8;
  658.                     if(clut->read.tested){
  659.                         if(clut->read.errors){
  660.                             if(clut->read.errorsAtOnce)sprintf(string,"bad");
  661.                             else sprintf(string,"!serial");
  662.                         }else if(grayClut!=clut->read.identity)sprintf(string,"ok");
  663.                         else sprintf(string,"!%s",colorGrayString[grayClut]);
  664.                     }else sprintf(string,"");
  665.                     ffprintf(o,s2,string);
  666.                     if(card->depth[d].pixelSize<=8)reportRGB=!clut->read.identity;
  667.                 }
  668.                 ffprintf(o,s3,"");
  669.                 if(reportRGB)ReportRGBGains(o,quickly,isGray,card);
  670.             }
  671.     
  672.             tested=0;
  673.             for(d=0;d<6;d++)if(card->depth[d].pixelSize)
  674.                 if(card->depth[d].clut[quickly][isGray].visual.tested)tested=1;
  675.             if(tested){
  676.                 sprintf(string,"%s visual inspection",setEntriesString[quickly]);
  677.                 ffprintf(o,s1,string);
  678.                 for(d=0;d<6;d++)if(card->depth[d].pixelSize){
  679.                     clut=&card->depth[d].clut[quickly][isGray];
  680.                     if(clut->visual.tested){
  681.                         if(clut->visual.errors){
  682.                             if(!clut->visual.errorsAtOnce)sprintf(string,"!serial");
  683.                             else sprintf(string,"bad");
  684.                         }else sprintf(string,"ok");
  685.                     }else sprintf(string,"");
  686.                     ffprintf(o,s2,string);
  687.                 }
  688.                 ffprintf(o,s3,"");
  689.             }
  690.         }
  691.     }
  692.     for(i=0;i<2;i++)if(o[i]!=NULL)fflush(o[i]);    // Save to disk, just in case.
  693. }
  694.  
  695. void ReportRGBGains(FILE *o[2],Boolean quickly,Boolean isGray,VideoInfo *card)
  696. {
  697.     short j,k,d,depths;
  698.     VideoCardClutTest *clut;
  699.     double rgbGain[3][3],rgbError[3];
  700.     Boolean integralGains,bad;
  701.  
  702.     depths=0;
  703.     for(j=0;j<3;j++){
  704.         for(k=0;k<3;k++)rgbGain[j][k]=0.;
  705.         rgbError[j]=0.0;
  706.     }
  707.     integralGains=1;
  708.     for(d=0;d<6;d++)if(card->depth[d].pixelSize && card->depth[d].pixelSize<=8){
  709.         clut=&card->depth[d].clut[quickly][isGray];
  710.         bad=0;
  711.         for(j=0;j<3;j++)bad|=clut->read.rgbError[j]>2.0*(1<<(16-4));
  712.         if(bad)continue;
  713.         for(j=0;j<3;j++){
  714.             for(k=0;k<3;k++)rgbGain[j][k]+=clut->read.rgbGain[j][k];
  715.             rgbError[j]+=clut->read.rgbError[j];
  716.         }
  717.         depths++;
  718.     }
  719.     for(j=0;j<3;j++){
  720.         for(k=0;k<3;k++){
  721.             rgbGain[j][k]/=depths;
  722.             integralGains&=(rgbGain[j][k]==floor(rgbGain[j][k]));
  723.         }
  724.         rgbError[j]/=depths;
  725.     }
  726.     if(IsFinite(rgbGain[0][0]))for(j=0;j<3;j++){
  727.         ffprintf(o," (%cOut±%4.1f%%)%c"
  728.             ,"RGB"[j],rgbError[j]*100./USHRT_MAX," = "[j]);
  729.         if(integralGains)ffprintf(o,"(%1.0f %1.0f %1.0f)"
  730.             ,rgbGain[j][0],rgbGain[j][1],rgbGain[j][2]);
  731.         else ffprintf(o,"(%3.2f %3.2f %3.2f)"
  732.             ,rgbGain[j][0],rgbGain[j][1],rgbGain[j][2]);
  733.         ffprintf(o,"%c(%cIn)\n"," x "[j],"RGB"[j]);
  734.     }
  735. }
  736.